#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "protocol.h"
#include "prot_port.h"
#include "msc_device.h"

#define USB_DISCONNECT_PIN  GPIO_NUM_10
#define RESP_BUF_SIZE (65 * 1024)   // 增加到512KB，确保有足够空间存放数据和协议头
static const char *TAG = "main";

// 声明回调函数
static void on_read_data(const uint8_t *data, uint32_t size);

// 定义回调函数
static void on_read_data(const uint8_t *data, uint32_t size)
{
    static uint8_t *resp_buf = NULL;
    if (!resp_buf) {
        resp_buf = malloc(RESP_BUF_SIZE);
    }
    
    int resp_len = proto_pack(resp_buf, RESP_BUF_SIZE, CMD_READ_FILE, data, size);
    if (resp_len > 0) {
        prot_uart_send(resp_buf, resp_len);
    }
    ESP_LOGI(TAG, "on_read_data: resp_len=%d , size=%lu", resp_len, size);
}

// 协议回调处理函数
static void handle_protocol_data(uint8_t cmd, const uint8_t *data, uint32_t len)
{
    static uint8_t *resp_buf = NULL;
    if (!resp_buf) {
        resp_buf = malloc(RESP_BUF_SIZE);
        if (!resp_buf) {
            ESP_LOGE(TAG, "Failed to allocate response buffer");
            return;
        }
    }

    if (!resp_buf) {
        ESP_LOGE(TAG, "Failed to allocate response buffer");
        return;
    }

    ESP_LOGI(TAG, "Received command: 0x%02X, data_len=%lu", cmd, len);
    int resp_len;

    switch(cmd) {
    case CMD_GET_DEVICE_INFO:
        ESP_LOGI(TAG, "Processing CMD_GET_DEVICE_INFO");
        struct serial_device_info dev_info;
        prot_get_device_info(&dev_info);
        resp_len = proto_pack(resp_buf, RESP_BUF_SIZE, cmd, (uint8_t*)&dev_info, sizeof(dev_info));
        if (resp_len > 0) {
            ESP_LOGI(TAG, "Sending device info response: %d bytes", resp_len);
            prot_uart_send(resp_buf, resp_len);
        } else {
            ESP_LOGE(TAG, "Failed to pack device info response");
        }
        break;

    case CMD_GET_UDISK_INFO: {
        struct serial_udisk_info udisk_info;
        const size_t megabyte = 1024 * 1024;
        msc_host_device_info_t info;
        msc_device_get_info(&info);
        uint64_t capacity = ((uint64_t)info.sector_size * info.sector_count) / megabyte;
        udisk_info.capacity = capacity;
        udisk_info.sector_count = info.sector_count;
        udisk_info.sector_size = info.sector_size;
        udisk_info.id_product = info.idProduct;
        udisk_info.id_vendor = info.idVendor;
        strncpy(udisk_info.manufacturer, info.iManufacturer, sizeof(udisk_info.manufacturer));
        strncpy(udisk_info.product, info.iProduct, sizeof(udisk_info.product));
        strncpy(udisk_info.serial_number, info.iSerialNumber, sizeof(udisk_info.serial_number));
        resp_len = proto_pack(resp_buf, RESP_BUF_SIZE, cmd, (uint8_t*)&udisk_info, sizeof(udisk_info));
        prot_uart_send(resp_buf, resp_len);
        break;
    }

    case CMD_GET_FILE_LIST: {
        struct serial_file_info *file_list = NULL;
        char path[128] = {0};
        memcpy(path, data, len > 127 ? 127 : len);
        ESP_LOGI(TAG, "CMD_GET_FILE_LIST: path = %s", path);
        
        // 限制最大文件数
        const int MAX_FILES = 100;
        int count = prot_get_file_list(path, &file_list, MAX_FILES);
        ESP_LOGI(TAG, "Got file list: count = %d", count);
        
        if (count > 0 && file_list) {
            // 计算实际需要发送的数据大小
            size_t resp_size = count * sizeof(struct serial_file_info);
            ESP_LOGI(TAG, "Response size will be: %d bytes", resp_size);
            
            if (resp_size <= RESP_BUF_SIZE - 10) {  // 预留协议头和CRC的空间
                resp_len = proto_pack(resp_buf, RESP_BUF_SIZE, cmd, 
                                    (uint8_t*)file_list, resp_size);
                ESP_LOGI(TAG, "Packed response length: %d", resp_len);
                
                if (resp_len > 0) {
                    if (prot_uart_send(resp_buf, resp_len)) {
                        ESP_LOGI(TAG, "File list sent successfully");
                    } else {
                        ESP_LOGE(TAG, "Failed to send file list");
                    }
                } else {
                    ESP_LOGE(TAG, "Failed to pack file list response");
                }
            } else {
                ESP_LOGE(TAG, "Response buffer too small for file list");
            }
        } else {
            ESP_LOGE(TAG, "Failed to get file list or empty directory");
        }
        
        if (file_list) {
            free(file_list);
        }
        break;
    }

    case CMD_MKDIR: {
        char path[128] = {0};
        memcpy(path, data, len > 127 ? 127 : len);
        if (prot_mkdir(path)) {
            resp_len = proto_pack(resp_buf, RESP_BUF_SIZE, cmd, (uint8_t*)"OK", 2);
            prot_uart_send(resp_buf, resp_len);
            ESP_LOGI(TAG, "CMD_MKDIR: resp_len = %d", resp_len);
        }
        ESP_LOGI(TAG, "CMD_MKDIR: path = %s", path);
        break;
    }

    case CMD_READ_FILE: {
        struct serial_read_request *req = (struct serial_read_request*)data;
        prot_read_file_async(req->path, req->offset, req->size, on_read_data);
        break;
    }

    case CMD_WRITE_FILE_START: {
        ESP_LOGI(TAG, "Received write start command, len=%lu", len);
        char path[128] = {0};
        memcpy(path, data, len > 127 ? 127 : len);
        ESP_LOGI(TAG, "Writing to path: %s", path);
        if (prot_write_file_start(path)) {
            resp_len = proto_pack(resp_buf, RESP_BUF_SIZE, cmd, (uint8_t*)"OK", 2);
            prot_uart_send(resp_buf, resp_len);
            ESP_LOGI(TAG, "Write start command processed successfully");
        } else {
            ESP_LOGE(TAG, "Write start command failed");
        }
        break;
    }

    case CMD_WRITE_FILE_DATA: {
        ESP_LOGI(TAG, "Received write data command, len=%lu", len);
        struct serial_write_content *content = (struct serial_write_content*)data;
        ESP_LOGI(TAG, "Write request: offset=%lu, size=%lu", content->offset, content->size);
        if (prot_write_file_data(content->offset, (uint8_t*)content->content, content->size)) {
            resp_len = proto_pack(resp_buf, RESP_BUF_SIZE, cmd, (uint8_t*)"OK", 2);
            prot_uart_send(resp_buf, resp_len);
            ESP_LOGI(TAG, "Write data command processed successfully resp_len = %d", resp_len);
        } else {
            ESP_LOGE(TAG, "Write data command failed");
        }
        break;
    }

    case CMD_WRITE_FILE_END: {
        ESP_LOGI(TAG, "Received write end command");
        uint8_t md5_hash[MD5_HASH_LENGTH];
        bool success = prot_write_file_end(md5_hash);
        if (success) {
            resp_len = proto_pack(resp_buf, RESP_BUF_SIZE, cmd, md5_hash, MD5_HASH_LENGTH);
            prot_uart_send(resp_buf, resp_len);
            ESP_LOGI(TAG, "Write end command processed successfully resp_len = %d", resp_len);
        } else {
            ESP_LOGE(TAG, "Write end command failed");
        }
        break;
    }
    }

    // free(resp_buf);
}

void app_main(void)
{
    // 初始化串口
    if (!prot_uart_init(UART_NUM_1, 2000000)) {
        ESP_LOGE(TAG, "Failed to init uart");
        return;
    }
    prot_uart_send((uint8_t*)"prot_uart_init success", 20);   

    // 设置协议处理回调函数
    prot_set_protocol_handler(handle_protocol_data);

    // 初始化MSC设备
    if (msc_device_init() != 0) {
        ESP_LOGE(TAG, "Failed to init MSC device");
        return;
    }

    // 设置 MSC 设备状态回调
    msc_device_set_state_callback(prot_set_udisk_inserted);

    // 主循环 - 只处理用户退出
    while (gpio_get_level(USB_DISCONNECT_PIN) != 0) {
        vTaskDelay(pdMS_TO_TICKS(100));
    }

    prot_deinit();
    msc_device_deinit();
}
